package com.liuyu.common.framework.message.service.impl.sms;

import com.google.common.collect.Lists;
import com.liuyu.common.framework.common.util.IdUtils;
import com.liuyu.common.framework.lang.exception.BusinessException;
import com.liuyu.common.framework.message.constant.MessageConstants;
import com.liuyu.common.framework.message.model.MobileCaptchaCmd;
import com.liuyu.common.framework.message.model.SmsSceneCmd;
import com.liuyu.common.framework.message.model.SmsSceneQueryCmd;
import com.liuyu.common.framework.message.model.SmsSceneVo;
import com.liuyu.common.framework.message.service.facade.MessageService;
import com.liuyu.common.framework.message.service.facade.SmsMobileVerifyService;
import com.liuyu.common.framework.message.strategy.context.MessageNotifyContext;
import com.liuyu.common.framework.message.strategy.model.NotifyMessage;
import com.liuyu.common.framework.web.response.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 短信消息服务
 *
 * @author: liuyu
 * @date: 2022/3/28 17:22
 * @version: 1
 */
@Service
@Slf4j
public class SmsMessageServiceImpl implements MessageService {
    private static final int INT_4 = 4;

    private static final String CAPTCHA_INTERVAL = ":captcha:interval:";
    private static final String CAPTCHA_COUNT = ":captcha:count:";
    private static final String CAPTCHA = ":captcha:";
    private static final String CAPTCHA_ERROR_COUNT = ":captcha:error:count:";
    private static final String PHONE_SCENE_MAX_COUNT = ":phone:scene:max:count";
    private static final String SUCCESS = "0";
    private static final String VALID_STATUS = "1";
    @Value("${captcha.length:4}")
    private Integer captchaLength = 4;
    @Value("${captcha.timeout:30}")
    private Integer captchaTimeOut;

    @Value("${captcha.interval:1}")
    private Integer captchaInterval;
    @Value("${captcha.timeout:欢迎使用中国人民财产保险公司东莞分公司的服务，验证码:%s,在%d分钟内有效.如非本人操作,请检查帐号安全。}")
    private String captchaTemplate;

    @Value("${captcha.scene.max.count:10}")
    private Integer sceneMaxCount;

    @Value("${captcha.error.count:3}")
    private Integer captchaErrorCount;


    @Autowired(required = false)
    private SmsMobileVerifyService mobileVerifyService;


    @Autowired
    private MessageNotifyContext messageNotifyContext;
    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 發送短信驗證碼
     *
     * @param mobileCaptchaCmd
     * @return com.liuyu.common.framework.web.response.ApiResponse
     * @author liuyu
     * @date 2022/3/28 17:22
     */
    @Override
    public ApiResponse sendMobileCaptcha(MobileCaptchaCmd mobileCaptchaCmd) {
        if (!redisTemplate.opsForSet().isMember(MessageConstants.PHONE_SMS_SCENE, mobileCaptchaCmd.getScene())) {
            return ApiResponse.fail("不支持该场景发送验证码");
        } else if (mobileVerifyService != null && !mobileVerifyService.verifyMobile(mobileCaptchaCmd.getScene(), mobileCaptchaCmd.getMobile())) {
            return ApiResponse.fail("手机号不在发送范围,请仔细核查");
        } else {
            String keys = mobileCaptchaCmd.getAppId() + CAPTCHA_INTERVAL + mobileCaptchaCmd.getMobile() + mobileCaptchaCmd.getScene();
            mobileCaptchaCmd.setCaptchaIntervalKey(keys);
            if (redisTemplate.hasKey(keys)) {
                return ApiResponse.fail("发送太频繁,请稍后再试!");
            } else {
                String key = mobileCaptchaCmd.getAppId() + CAPTCHA_COUNT + mobileCaptchaCmd.getMobile() + mobileCaptchaCmd.getScene();
                mobileCaptchaCmd.setCaptchaCountKey(key);
                Integer count = (Integer) redisTemplate.opsForValue().get(key);
                if (count == null) {
                    count = 0;
                }
                mobileCaptchaCmd.setCount(count);
                String key1 = mobileCaptchaCmd.getAppId() + PHONE_SCENE_MAX_COUNT;
                Integer maxCount = (Integer) redisTemplate.opsForValue().get(key1);
                if (maxCount == null) {
                    maxCount = sceneMaxCount;
                }

                if (count < maxCount) {
                    ApiResponse result = sendSms(mobileCaptchaCmd);
                    return result;
                } else {
                    return ApiResponse.fail("短信超过最大限制，请明天再试");
                }
            }
        }
    }

    /**
     * 新增短信场景
     *
     * @param smsSceneCmd
     * @return com.liuyu.common.framework.web.response.ApiResponse
     * @author liuyu
     * @date 2022/3/28 22:26
     */
    @Override
    public ApiResponse saveSmsScene(SmsSceneCmd smsSceneCmd) {
        if (redisTemplate.opsForSet().isMember(MessageConstants.PHONE_SMS_SCENE, smsSceneCmd.getScene())) {
            return ApiResponse.fail("该场景已经存在不需要创建");
        }
        redisTemplate.opsForSet().add(MessageConstants.PHONE_SMS_SCENE, smsSceneCmd.getScene());
        return ApiResponse.success("场景创建成功");
    }

    /**
     * 验证短信验证码
     *
     * @param mobileCaptchaCmd
     * @return com.liuyu.common.framework.web.response.ApiResponse
     * @author liuyu
     * @date 2022/4/2 9:51
     */
    @Override
    public ApiResponse checkMobileCaptcha(MobileCaptchaCmd mobileCaptchaCmd) {
        String key = mobileCaptchaCmd.getAppId() + CAPTCHA + mobileCaptchaCmd.getMobile() + mobileCaptchaCmd.getScene();
        String captcha = (String) redisTemplate.opsForValue().get(key);
        if (StringUtils.isBlank(captcha)) {
            throw new BusinessException("短信验证未生成或者失效,请重新生成");
        }
        //校验错误次数
        String key1 = mobileCaptchaCmd.getAppId() + CAPTCHA_ERROR_COUNT + mobileCaptchaCmd.getMobile() + mobileCaptchaCmd.getScene();
        Integer count = (Integer) redisTemplate.opsForValue().get(key1);
        if (count == null) {
            count = 0;
        }
        if (count < captchaErrorCount) {
            if (!mobileCaptchaCmd.getCaptcha().equals(captcha)) {
                count++;
                if (redisTemplate.hasKey(key1)) {
                    redisTemplate.opsForValue().set(key1, count);
                } else {
                    Long expire = redisTemplate.getExpire(key, TimeUnit.SECONDS);
                    redisTemplate.opsForValue().set(key1, count, expire, TimeUnit.SECONDS);
                }
                throw new BusinessException("短信验证码不正确");
            }
            return ApiResponse.success("短信验证成功");
        } else {
            throw new BusinessException("短信验证错误次数超过最大重试次数,请重新生成");
        }
    }

    /**
     * 删除短信验证码
     *
     * @param mobileCaptchaCmd
     * @return com.liuyu.common.framework.web.response.ApiResponse
     * @author liuyu
     * @date 2022/4/11 23:50
     */
    @Override
    public ApiResponse deleteMobileCaptcha(MobileCaptchaCmd mobileCaptchaCmd) {
        String key = mobileCaptchaCmd.getAppId() + CAPTCHA + mobileCaptchaCmd.getMobile() + mobileCaptchaCmd.getScene();
        //校验错误次数
        String key1 = mobileCaptchaCmd.getAppId() + CAPTCHA_ERROR_COUNT + mobileCaptchaCmd.getMobile() + mobileCaptchaCmd.getScene();
        //验证成功，删除验证码以及错误次数
        redisTemplate.delete(key);
        redisTemplate.delete(key1);
        return ApiResponse.success("短信验证码删除成功");
    }

    /**
     * @param smsSceneCmd
     * @return
     */
    @Override
    public List<SmsSceneVo> listSmsScene(SmsSceneQueryCmd smsSceneCmd) {
        Cursor<String> sets = redisTemplate.opsForSet().scan(MessageConstants.PHONE_SMS_SCENE, ScanOptions.NONE);
        List<SmsSceneVo> list = Lists.newArrayList();
        while (sets.hasNext()) {
            list.add(new SmsSceneVo(sets.next(), VALID_STATUS));
        }
        return list;
    }

    private ApiResponse sendSms(MobileCaptchaCmd mobileCaptchaCmd) {
        String captcha = IdUtils.creatRandomNum(captchaLength);
        NotifyMessage notifyMessage = new NotifyMessage();
        notifyMessage.setSubject("密码重置");
        notifyMessage.setContent(String.format(captchaTemplate, captcha, captchaTimeOut));
        List<String> to = Lists.newArrayListWithCapacity(1);
        to.add(mobileCaptchaCmd.getMobile());
        notifyMessage.setNotifyServerType("sms");
        notifyMessage.setRecipients(to);
        ApiResponse result = messageNotifyContext.send(notifyMessage);
        Integer count = mobileCaptchaCmd.getCount();
        if (SUCCESS.equals(result.getCode())) {
            //设置发送间隔
            redisTemplate.opsForValue().set(mobileCaptchaCmd.getCaptchaIntervalKey(), 1, captchaInterval, TimeUnit.MINUTES);
            count = count + 1;
            //设置发送次数
            if (redisTemplate.hasKey(mobileCaptchaCmd.getCaptchaCountKey())) {
                redisTemplate.opsForValue().set(mobileCaptchaCmd.getCaptchaCountKey(), count);
            } else {
                redisTemplate.opsForValue().set(mobileCaptchaCmd.getCaptchaCountKey(), count, 86400L, TimeUnit.SECONDS);
            }
            String key1 = mobileCaptchaCmd.getAppId() + CAPTCHA + mobileCaptchaCmd.getMobile() + mobileCaptchaCmd.getScene();
            //添加缓存中并在制定时间内失效
            redisTemplate.opsForValue().set(key1, captcha, captchaTimeOut, TimeUnit.MINUTES);
            String key2 = mobileCaptchaCmd.getAppId() + CAPTCHA_ERROR_COUNT + mobileCaptchaCmd.getMobile() + mobileCaptchaCmd.getScene();
            //删除错误次数
            redisTemplate.delete(key2);
            result.setMessage("验证码到发送到手机号:" + mobileCaptchaCmd.getMobile());
        }
        return result;
    }


}
